package com.sunriz.motordrive.server.photoVoltaic.Impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sunriz.motordrive.domain.DAO.PvHcInfoDAO;
import com.sunriz.motordrive.domain.DTO.SetValueDTO;
import com.sunriz.motordrive.domain.DTO.Result;
import com.sunriz.motordrive.mapper.PvHcMapper;
import com.sunriz.motordrive.server.common.SetValueServer;
import com.sunriz.motordrive.server.common.impl.BaseSerialImpl;
import com.sunriz.motordrive.server.photoVoltaic.PhotovoltaicHcServer;
import com.sunriz.motordrive.utils.ArrayUtils;
import com.sunriz.motordrive.utils.Crc;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

/**
 * ClassName: PhotoVoltaicHcImpl
 * Package: com.sunriz.motordrive.server.photoVoltaic.Impl
 * Description:
 *      光伏hc数据收发
 * @Author BuTang
 * @Create 2024/7/2 11:50
 * @Version 1.0
 */
@Slf4j
@Service
public class PhotovoltaicHcServerImpl implements PhotovoltaicHcServer {

    @Autowired
    BaseSerialImpl serialPortImpl;
    @Autowired
    SetValueServer setValue;

    @Resource
    PvHcMapper pvHcMapper;

    @Autowired
    StringRedisTemplate redisTemplate;

    /**
     * Key是寄存器地址，Value是寄存器数量
     * 前端修改展示逻辑后，可以删除这个table1与table2，以后前端根据接收到的数据的tableNumber区分
     */
    public static Map<Integer,Integer> table1 = new HashMap<>();
    public static Map<Integer,Integer> table2 = new HashMap<>();
    public static Map<Integer,Integer> allInfo = new HashMap<>();
    private static String EXCEL_FILEPATH = "./excel/photoVoltaic/config.xlsx";


    /**
     * v2版本
     * 16位和32位寄存器分开存储
     */
    public Map<Integer, PvHcInfoDAO> result = new HashMap<>();
    public List<PvHcInfoDAO> allConfig ;
    public static int slaveId = 1;

    //    三十二位寄存器点位
    public static ArrayList byte32 = new ArrayList();



    static {
//        mapOnlyRead.put(0,2);
//        mapOnlyRead.put(2,16);
//        mapOnlyRead.put(18,4);
//        mapOnlyRead.put(22,2);
//        mapOnlyRead.put(86,2);
//        mapOnlyRead.put(150,4);
//        mapOnlyRead.put(1037,6);
//
//        table2.put(1001,26);
//        table2.put(1027,2);
//       table2.put(1029,2);
//       table2.put(1033,2);
//       table2.put(1043,2);
//        byte32.add(0);
//        byte32.add(18);
//        byte32.add(20);
//        byte32.add(1027);

//        eachAddrByte32.add(0);
//        eachAddrByte32.add(18);
//        eachAddrByte32.add(20);
//        eachAddrByte32.add(1027);
    }

    /**
     * bean加载完成后执行
     */
    @PostConstruct
    public void initHcInfo(){

        List<Integer> table1List16 = new ArrayList();
        List<Integer> table1List32 = new ArrayList();
        List<Integer> table2List16 = new ArrayList();
        List<Integer> table2List32 = new ArrayList();

        List<Integer> list16 = new ArrayList();
        List<Integer> list32 = new ArrayList();

        // 获取表格内的寄存器地址
        List<PvHcInfoDAO> allConfig =  new ArrayList();
        try {

            try (InputStream fis = new FileInputStream(EXCEL_FILEPATH);
                 Workbook workbook = new XSSFWorkbook(fis)) {

                // 获取第一个工作表
                Sheet sheet = workbook.getSheet("PvHcInfo");

                for (int i = 1; i < sheet.getLastRowNum(); i++) {
                    PvHcInfoDAO hcInfo = new PvHcInfoDAO();
                    Row row = sheet.getRow(i);
                    hcInfo.address = (int)row.getCell(0).getNumericCellValue();
                    hcInfo.name = row.getCell(1).getStringCellValue();
                    hcInfo.signed = row.getCell(2).getBooleanCellValue();
                    hcInfo.precision = row.getCell(3).getNumericCellValue();
                    hcInfo.numberOfBytes = (int)row.getCell(4).getNumericCellValue();
                    hcInfo.readOrWrite = row.getCell(5).getStringCellValue();
                    hcInfo.tableNumber = (int)row.getCell(6).getNumericCellValue();
                    hcInfo.describe = row.getCell(7).getStringCellValue();
                    allConfig.add(hcInfo);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }
        for (PvHcInfoDAO pvHcInfo : allConfig) {

            ObjectMapper objectMapper = new ObjectMapper();


            try {
                String s = objectMapper.writeValueAsString(pvHcInfo);
//                redisTemplate.opsForValue().set("group1:group2:"+pvHcInfo.getAddress(),s);
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }


            // 初始化返回值
            result.put(pvHcInfo.getAddress(),pvHcInfo);

            Integer tableNumber = pvHcInfo.tableNumber;
            Integer numberOfBytes = pvHcInfo.numberOfBytes;

            // 取消table1与table2
            if (numberOfBytes.equals(1)){
                list16.add(pvHcInfo.address);
            }else {
                list32.add(pvHcInfo.address);
            }

            if (tableNumber.equals(1) && numberOfBytes.equals(1)){
                table1List16.add(pvHcInfo.address);
            }else if (tableNumber.equals(1) && numberOfBytes.equals(2)){
                table1List32.add(pvHcInfo.address);
            }else if (tableNumber.equals(2) && numberOfBytes.equals(1)){
                table2List16.add(pvHcInfo.address);
            }else if (tableNumber.equals(2) && numberOfBytes.equals(2)){
                table2List32.add(pvHcInfo.address);
            }
        }


        BaseSerialImpl.result = result;

        // 取消table1与table2
        list16.stream().sorted();
        list32.stream().sorted();

        table1List16.stream().sorted();
        table1List32.stream().sorted();
        table2List16.stream().sorted();
        table2List32.stream().sorted();

        distinguishBetweenTableAndBytes(table1List16,table1,1);
        distinguishBetweenTableAndBytes(table1List32,table1,2);
        distinguishBetweenTableAndBytes(table2List16,table2,1);
        distinguishBetweenTableAndBytes(table2List32,table2,2);

        // 所有的寄存器查询
//        table1.forEach((start, length) -> allInfo.put(start,length));
//        table2.forEach((start, length) -> allInfo.put(start,length));

        // 取消table1与table2
        distinguishBetweenTableAndBytes(list16,allInfo,1);
        distinguishBetweenTableAndBytes(list32,allInfo,2);
        list32.forEach(address -> byte32.add(address));

        log.info("初始化寄存器信息完成");
    }

    /**
     *  将每一个寄存器点位，转换为，起始地址+数量的形式存放至map中
     * @param list 数据
     * @param map 存放的map表
     */
    private static void distinguishBetweenTableAndBytes(List list,Map<Integer,Integer> map , int bytesNumber ) {

        int startAddress = (int) list.get(0);
        int currentAddress = (int) list.get(0);
        int number = bytesNumber ; ;
//        int number = bytesNumber ;
        for (int i = 1; i < list.size(); i++) {
            if (list.get(i).equals(currentAddress+1)){
                currentAddress = (int) list.get(i);
                number += bytesNumber;
            } else {
                map.put(startAddress, number);
                startAddress = (int) list.get(i);
                currentAddress = (int) list.get(i);
                number = bytesNumber;
            }
            if (i == list.size()-1 ){
                map.put(startAddress, number);

            }
        }
    }

    @Override
    public Result getOnlyReadData() {
        jssc.SerialPort myPort = BaseSerialImpl.serialPort;
        // 判断端口是否开启，若开启，则开始定时查询数据
        if (myPort != null) {
            if (myPort.isOpened() == true ) {
                table1.forEach((integer, integer2) -> serialPortImpl.sendModbus(ArrayUtils.fillIn0304Msg(slaveId,3,integer,integer2)));
            }else {
                log.info("端口尚未开启，无法读取数据");
            }
        }else {
            log.info("端口尚未开启，无法读取数据");
        }
        return Result.success(BaseSerialImpl.returnRead);
    }

    @Override
    public Result getReadAndWrite() {
        jssc.SerialPort myPort = BaseSerialImpl.serialPort;
        // 判断端口是否开启，若开启，则开始定时查询数据
        if (myPort != null) {
            if (myPort.isOpened() == true ) {
                table2.forEach((integer, integer2) -> serialPortImpl.sendModbus(ArrayUtils.fillIn0304Msg(slaveId,3,integer,integer2)));
            }else {
                log.info("端口尚未开启，无法读取数据");
            }
        }else {
            log.info("端口尚未开启，无法读取数据");
        }
        return Result.success(BaseSerialImpl.returnReadAndWrite);
    }


    /**
     * 设置电机模式
     * @param setValueDTO 地址和值
     * @return resultVO
     */
    @Override
    public Result setMotorStatus(SetValueDTO setValueDTO) {
        return setValue.single(setValueDTO);
    }

    @Override
    public Result getData() {
        // todo 精度转换与正负号转换
        result.forEach((BiConsumer<Integer, Object>) (integer, o) -> {
            PvHcInfoDAO info = (PvHcInfoDAO) o;
            double precision = info.getPrecision();
            double NumberOfBytes = info.getNumberOfBytes();
            boolean signed = info.signed;
            double value = info.value;

            if (signed == true){
                if (NumberOfBytes == 1 ){
                    char signedValue = (char) value;
                }else {
                    // 将无符号的32位整数转换为有符号的32位整数
                    if (value > Integer.MAX_VALUE) {
                        // 如果无符号值大于 Integer.MAX_VALUE，则减去 2^32 来得到有符号的负数值
                        value = (int) (value - 4294967296L);
                    } else {
                        // 否则直接转换为 int（正数）
                        value = (int) value;
                    }
                }
            }
            value *= precision;
            ((PvHcInfoDAO) o).setValue(value);
        });
        return Result.success(result);
    }

    @Override
    public Result updateVersionDemo() {
        BaseSerialImpl.MsgFlag = true;
        try {
            BaseSerialImpl.maxRetries = 1;

            // 文件路径
            String filePath = System.getProperty("user.dir") + "/binFile/" + "PVTC_bootloader.bin";
            try (FileInputStream fis = new FileInputStream(filePath)) {
                List<byte[]> byteArrays = new ArrayList<>();
                // 固定头部字节
                byte[] header = new byte[]{0x0B, 0x10, 0x1F, 0x40, 0x00, (byte) 0x40, (byte) 0x80};

                // 用来读取每次128个字节
                byte[] buffer = new byte[128];
                int bytesRead;

                while ((bytesRead = fis.read(buffer)) != -1) {

                    header[4] = (byte) (bytesRead/2 >> 8);
                    header[5] = (byte) (bytesRead/2 & 0xFF);
                    header[6] = (byte) bytesRead;

                    // 创建一个新的字节数组，大小为6（头部字节）+ 128（文件字节）
                    byte[] byteArray = new byte[header.length + bytesRead];


                    // 将头部字节复制到新数组中
                    System.arraycopy(header, 0, byteArray, 0, header.length);
                    // 将读取到的文件字节复制到新数组中
                    System.arraycopy(buffer, 0, byteArray, header.length, bytesRead);

                    byteArrays.add(byteArray);  // 将这个数组添加到列表中
                }

                // 输出结果（每个字节数组以16进制方式输出）
//            for (byte[] byteArray : byteArrays) {
//                for (byte b : byteArray) {
//                    System.out.print(String.format("%02X ", b));
//                }
//                System.out.println();  // 每个byte[]之间换行
//            }

                // 下发报文
                byteArrays.forEach(bytes -> serialPortImpl.sendModbus(bytes));
                // 文件crc校验报文
                String s = Crc.calculateFileCRC16(filePath);
                int i1 = Integer.parseInt(s.substring(0, 2), 16);
                int i2 = Integer.parseInt(s.substring(2, 4), 16);
                byte[] crcMsg = new byte[] {(byte) slaveId, 0x10, 0x1F, 0x40, 0x00, 0x01,0x02, (byte) i1, (byte) i2}; // 固定头部字节
                serialPortImpl.sendModbus(crcMsg);

            } catch (IOException e) {
                e.printStackTrace();
            }
            return Result.success();
        }finally {
            BaseSerialImpl.maxRetries = 3;
        }
    }

    @Override
    public Result updateVersion(MultipartFile file) {

        try {
            String originalFilename = file.getOriginalFilename();
            if (!originalFilename.toLowerCase().endsWith(".bin")){
                return Result.fail(String.valueOf(400),"文件必须是.bin格式");
            }

            BaseSerialImpl.maxRetries = 1;
            // 使用 MultipartFile 的 InputStream 读取文件
            try (InputStream is = file.getInputStream()) {
                List<byte[]> byteArrays = new ArrayList<>();
                byte[] header = new byte[] {(byte) slaveId, 0x10, 0x1F, 0x40, 0x00, (byte) 0x40, (byte) 0x80 }; // 固定头部字节

                byte[] buffer = new byte[128];  // 用来读取每次128个字节
                int bytesRead;

                // 读取文件内容并处理
                while ((bytesRead = is.read(buffer)) != -1) {
                    // 创建一个新的字节数组，大小为6（头部字节）+ 128（文件字节）
                    byte[] byteArray = new byte[header.length + bytesRead];
                    System.arraycopy(header, 0, byteArray, 0, header.length);  // 将头部字节复制到新数组中
                    System.arraycopy(buffer, 0, byteArray, header.length, bytesRead);  // 将读取到的文件字节复制到新数组中

                    byteArrays.add(byteArray);  // 将这个数组添加到列表中
                }

                // 输出结果（每个字节数组以16进制方式输出）
                for (byte[] byteArray : byteArrays) {
                    for (byte b : byteArray) {
                        System.out.print(String.format("%02X ", b));
                    }
                    System.out.println();  // 每个byte[]之间换行
                }
                // 下发报文
                byteArrays.forEach(bytes -> serialPortImpl.sendModbus(bytes));

                // 文件crc校验报文
                byte[] bytes = file.getBytes();
                String s = Crc.Crc16(bytes);
                int i1 = Integer.parseInt(s.substring(0, 2), 16);
                int i2 = Integer.parseInt(s.substring(2, 4), 16);
                byte[] crcMsg = new byte[] {(byte) slaveId, 0x10, 0x1F, 0x40, 0x00, 0x01,0x02, (byte) i1, (byte) i2}; // 固定头部字节
                serialPortImpl.sendModbus(crcMsg);

            } catch (IOException e) {
                e.printStackTrace();
                return Result.fail("404", "文件读取失败");
            }
            return Result.success();
        }finally {
            BaseSerialImpl.maxRetries = 3;
        }

//        if (file.isEmpty()) {
//            return Result.fail (String.valueOf(404),"No file uploaded");
//        }
//
//        try {
//            // 获取文件名
//            String fileName = file.getOriginalFilename();
//            if (fileName == null) {
//                return Result.fail (String.valueOf(404),"Invalid file");
//            }
//
//            String UPLOAD_DIR = System.getProperty("user.dir") + "/binFile/"; // 获取当前工作目录
//
//            // 构建文件保存路径
//            File targetFile = new File(UPLOAD_DIR + fileName);
//
//
//
//            // 将文件保存到目标位置
//            file.transferTo(targetFile);
//
//            return Result.success();
//        } catch (Exception e) {
//            e.printStackTrace();  // 打印错误日志，帮助排查具体问题
//            return Result.fail("404","11111");
//        }
    }
}
